﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using ODataRegression.Framework.Core.Shared;
using System.Diagnostics;
using System.Threading;
using System.Net;
using System.Configuration;
using ODataTestFrameworkCore.shared;
namespace ODataRegression.Framework.Core.TestOM
{
    public enum ContentVerificationResult
    {
        PASSED,
        FAILED,
        UNKNOWN
    }

    public enum TestExecutionStatus
    {
        Pending,
        InProgress,
        Completed,
        Canceled
    }

    public class ODataTest : ICloneable
    {
        private string testGUID;
        private string serviceRoot;
        private string serviceBusinessName;
        private string entityname;
        private string entityCollectionName;
        private string testSortName;
        private string testTitle;
        public string queryURL;
        private UriResultType expectedResultType;
        private Regex RegExVerificationToken;



       // private TestConfiguration testConfiguration;

        public ODataTestResult TestResult { get; set; }


        public Exception err;
        public Dictionary<string, string> ResultBag = new Dictionary<string, string>();

        int maxOutputLimit = Convert.ToInt32(ConfigurationSettings.AppSettings["MaxOutputLimit"]);


        public ODataTest(string serviceRoot, string serviceBusinessName, string entityCollectionName, string entityName, string testSortName, string testTitle,
            string queryURL, UriResultType expectedResultType, Regex verificationRegEx )
        {
            this.TestResult = new ODataTestResult(DateTime.Now, DateTime.Now, "OK", null, null);
            this.testGUID = Guid.NewGuid().ToString();
            this.serviceRoot = serviceRoot;
            this.serviceBusinessName = serviceBusinessName;
            this.entityCollectionName = entityCollectionName;
            this.entityname = entityName;
            this.testSortName = testSortName;
            this.testTitle = testTitle;
            this.queryURL = queryURL;

            this.TestResult.testExecutionStatus = TestExecutionStatus.Pending;
            //this.result = null;
            this.expectedResultType = expectedResultType;
            this.TestResult.actualResultType = UriResultType.None;

            this.RegExVerificationToken = verificationRegEx;
            //this.testConfiguration = testconfig;
            //this.TestResult.ContentTypeVerificationStatus = ContentVerificationResult.UNKNOWN;


        }

        public void Execute(Object stateInfo)
        {

            this.TestResult.testExecutionStatus = TestExecutionStatus.InProgress;
            bool traceOnly = Convert.ToBoolean(TestConfigReader.TestEngineSettings[TestEngineSettingsTypes.TraceOnly]);

            /// Throw exception if URL is more than 180 char (Max WCF Request limit)
            //if (this.queryURL.Length>180)
            //{
            //    throw new ArgumentException(string.Format("Date Service does not support URLs more than 180 char long Requested URL Length={0}",this.queryURL.Length));
            //}
            if (this.testSortName == ODataTestNames.DownloadAllRecords.ToString())
            {
                this.TestResult = HTTPHelpers.DownloadAllFeeds(this.queryURL);
            }
            else
            {
                this.TestResult = HTTPHelpers.ExecuteGet(this.queryURL, traceOnly);
                this.TestResult.OriginalResponceString = this.RetriveResponceContent();
                

                this.VerifyContentType();
                this.VerifyOutputForRegex();
            }


            this.TestResult.testExecutionStatus = TestExecutionStatus.Completed;
            this.err = TestResult.expection;
            this.PrepareResultBag();
            this.TestResult.testExecutionStatus = TestExecutionStatus.Completed;

        }

        private void PrepareResultBag()
        {
            ResultBag.Add("TestGUID", testGUID);

            ResultBag.Add("ServiceRoot", serviceRoot);
            ResultBag.Add("ServiceBusinessName", serviceBusinessName);

            ResultBag.Add("EntityCollectionName", entityCollectionName);
            ResultBag.Add("EntityName", entityname);

            ResultBag.Add("TestName", testSortName);
            ResultBag.Add("TestTitle", testTitle);

            ResultBag.Add("StartTime", TestResult.StartTime.ToString());
            ResultBag.Add("EndTIme", TestResult.EndTIme.ToString());
            ResultBag.Add("Duration", ((TestResult.EndTIme - TestResult.StartTime).TotalMilliseconds / (60 * 1000)).ToString("F4"));

            ResultBag.Add("ExpectedResultType", this.expectedResultType.ToString());
            ResultBag.Add("ActualResultType", "Not implemented yet");
            ResultBag.Add("ContentTypeVerificationStatus", "Not implemented yet");

            ResultBag.Add("HttpStatusCode", TestResult.statusCode);
            ResultBag.Add("TestResult", GetFinalTestResult());

            ResultBag.Add("QueryURL", queryURL);
            ResultBag.Add("ErrorMessage", GetErrorMassage());
            ResultBag.Add("ErrorTrace", GetErrorTrace());
            ResultBag.Add("Output", GetTrimmedResponce(this.TestResult.OriginalResponceString));
        }

        private string RetriveResponceContent()
        {
            string resp = "None";
            // TODO Retrive Responce content 
            if (TestResult != null && TestResult.webResp != null)
            {
                StreamReader sr = new StreamReader(TestResult.webResp.GetResponseStream());
                resp = sr.ReadToEnd();
                TestResult.webResp.Close();
            }
            return resp;
        }

        private void VerifyOutputForRegex()
        {
            //TODO additional Verification on get result 
            // this.OutputVerificationStatus = ResultStatus.PASSED;
            if (err == null)
            {
                this.TestResult.ContentTypeVerificationStatus = ContentVerificationResult.PASSED;
                try
                {
                    if (RegExVerificationToken != null)
                    {
                        if (this.TestResult.OriginalResponceString != "None" && !RegExVerificationToken.Match(this.TestResult.OriginalResponceString).Success)
                        {
                            //ContentTypeVerificationStatus = ResultStatus.FAILED;

                            this.TestResult.ContentTypeVerificationStatus = ContentVerificationResult.FAILED;
                            string error = string.Format("Regex Match on responce content failed Regex={0}", RegExVerificationToken.ToString());
                            throw new ApplicationException(error);
                        }
                    }
                }
                catch (Exception ex)
                {
                    err = ex;

                }
            }

        }

        private void VerifyContentType()
        {
            //this.ContentTypeVerificationStatus = ContentVerificationResult.UNKNOWN;
            //this.ContentTypeVerificationStatus=(this.expectedResultType==this.actualResultType)?ContentVerificationResult.PASSED:ContentVerificationResult.FAILED;

            //TODO Not implemented yet
        }
        private string GetTrimmedResponce(string Response)
        {
            string responceContent = Response;
            if (this.testSortName == "DownloadAllRecords")
            {
                responceContent = this.TestResult.OriginalResponceString;
            }
            else
            {
                //Reposition feed to <title> element to trim the unnecessary schema info 
                if (responceContent != null)
                {
                    if (responceContent.Contains("<title type"))
                    {
                        responceContent = responceContent.Substring(responceContent.IndexOf("<title type"));
                    }
                    if (responceContent.Contains("<m:properties>"))
                    {
                        int firstOutputIndex = responceContent.IndexOf("<m:properties>");
                        int firstOutputLength = responceContent.IndexOf("</m:properties>") - (firstOutputIndex - "</m:properties>".Length);
                        responceContent = responceContent.Substring(firstOutputIndex, firstOutputLength);
                        // responceContent = responceContent.Replace(Environment.NewLine,string.Empty);
                        // responceContent = responceContent.Substring(0,responceContent.IndexOf(""));

                    }

                    //if (responceContent.Length > maxOutputLimit)
                    //{
                    //    responceContent = responceContent.Substring(0, maxOutputLimit) + ".............Response Truncated";
                    //}

                }
                else
                {
                    responceContent = "None";
                }
            }

            return responceContent;
        }

        private string GetFinalTestResult()
        {


            string testResult = TestExecutionStatus.Pending.ToString();
            if (this.TestResult.testExecutionStatus == TestExecutionStatus.Completed)
            {
                if (this.testSortName == "DownloadAllRecords" && this.TestResult.statusCode == "OK")
                {
                    testResult = "PASSED";
                }
                else
                {
                    testResult = "Unknown";

                    if (TestResult.webResp != null)
                    {

                        if (TestResult.webResp.StatusCode == HttpStatusCode.OK)
                        {
                            testResult = "PASSED";
                            if (this.TestResult.ContentTypeVerificationStatus != ContentVerificationResult.PASSED)
                            {
                                testResult = "ERROR";
                            }

                        }
                    }
                    else
                    {
                        testResult = "ERROR";
                    }
                }


            }
            else
            {
                testResult = this.TestResult.testExecutionStatus.ToString();
            }

            // Sometimes there is no HTTP Exception but responce is Fault XML that need to be handled
            if (this.TestResult.OriginalResponceString.Contains("<Fault"))
            {
                testResult = "ERROR";
            }
            return testResult;
        }

        private string GetErrorMassage()
        {

            string errorMessage = "None";
            if (TestResult != null && this.err != null)
            {
                errorMessage = this.err.Message;
            }

            return errorMessage;
        }

        private string GetErrorTrace()
        {

            string errorTrace = "None";
            if (TestResult != null && this.err != null)
            {
                if (typeof(WebException) == this.err.GetType() && ((WebException)this.err).Response!=null)
                {
                    WebException newErr = (WebException)this.err;
                    StreamReader sr = new StreamReader(newErr.Response.GetResponseStream());
                    errorTrace = sr.ReadToEnd();
                }
                else
                {
                    errorTrace = "Error :" + this.err.Message + this.err.StackTrace;
                    if (this.err.InnerException != null)
                    {
                        errorTrace = "Error :" + this.err.InnerException.Message + this.err.InnerException.StackTrace;
                    }
                }
            }

            return errorTrace;
        }

        public object Clone()
        {
            ODataTest tempTest = (ODataTest)this.MemberwiseClone();
            tempTest.testGUID = Guid.NewGuid().ToString();
            tempTest.ResultBag = new Dictionary<string, string>();
            return tempTest;
        }
    }
}
